package arithm

import (
	"errors"
	"fmt"
	"math"
	"math/big"
	"regexp"
	"strconv"
	"strings"
)

func readComplex(s string) (Number, error) {
	realPart, imagPart := getComplexParts(s)
	var r, i float64
	if realPart == "" {
		r = 0
	} else {
		x, err := ReadNumber(realPart)
		if err != nil {
			return nil, err
		}
		r = numberIntoFloat64(x)
	}
	switch imagPart {
	case "+i":
		i = 1
	case "-i":
		i = -1
	case "":
		i = 0
	default:
		x, err := ReadNumber(imagPart)
		if err != nil {
			return nil, err
		}
		i = numberIntoFloat64(x)
	}

	return Complex(complex(r, i)), nil
}

func numberIntoFloat64(x Number) float64 {
	var r float64
	switch x := x.(type) {
	case *BigInteger:
		r, _ = (*big.Int)(x).Float64()
	case *Rational:
		r, _ = (*big.Rat)(x).Float64()
	case Integer:
		r = float64(int64(x))
	case Real:
		r = float64(x)
	default:
		return math.NaN()
	}
	return r
}

func readReal(s string) (Number, error) {
	if !strings.Contains(s, ".") && !strings.Contains(s, "e") && !strings.Contains(s, "E") {
		return nil, fmt.Errorf("%s is an invalid real format", s)
	}
	x, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return nil, fmt.Errorf("%s is an invalid real format", s)
	}
	return Real(x), nil
}

func readRational(s string) (Number, error) {
	if !strings.Contains(s, "/") {
		return nil, fmt.Errorf("%s is not a/b pattern", s)
	}
	x := new(big.Rat)
	_, e := x.SetString(s)
	if !e {
		return nil, fmt.Errorf("%s is an invalid rational format", s)
	}
	return (*Rational)(x), nil
}

func readBigInteger(s string) (Number, error) {
	x := new(big.Int)
	_, e := x.SetString(s, 10)
	if !e {
		return nil, fmt.Errorf("%s is an invalid big integer format", s)
	}
	return (*BigInteger)(x), nil
}

func readInteger(s string) (Number, error) {
	x, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("%s is an invalid integer format", s)
	}
	return Integer(x), nil
}

func ReadNumber(s string) (Number, error) {
	nt, err := getNumberType(s)
	if err != nil {
		return nil, err
	}
	switch nt {
	case COMPLEX_TYPE:
		return readComplex(s)
	case REAL_TYPE:
		return readReal(s)
	case RATIONAL_TYPE:
		return readRational(s)
	case BIGINTEGER_TYPE:
		return readBigInteger(s)
	case INTEGER_TYPE:
		return readInteger(s)
	}
	return nil, errors.New("unknown number format")
}

// getNumberType determines which type of number a string represents
func getNumberType(s string) (NumberType, error) {
	// Check for complex number (contains 'i')
	if strings.Contains(s, "i") {
		// Validate that it's actually a complex number format
		// A valid complex number should match patterns like:
		// "i", "+i", "-i", "3i", "3+4i", "3-4i", "3.5+4.2i", "1/2+3/4i", "1e2+3e4i", etc.

		// Handle special cases first
		if s == "i" || s == "+i" || s == "-i" {
			return COMPLEX_TYPE, nil
		}

		// Check if it's a valid complex number format using regex
		// Pattern explanation:
		// ^[+-]? - Optional sign at the beginning
		// (?:[0-9]*\.?[0-9]+(?:[eE][+-]?[0-9]+)?(?:/[0-9]+)? - Real part (optional) with various formats
		// [+-] - Required operator between real and imaginary parts (unless no real part)
		// [0-9]*\.?[0-9]+(?:[eE][+-]?[0-9]+)?(?:/[0-9]+)? - Imaginary part coefficient
		// i$ - Ending with i

		// Complex patterns:
		// 1. Pure imaginary: 3i, -3i, +3i
		// 2. Full complex: 3+4i, 3-4i, -3+4i, 3.5-4.2i, 1/2+3/4i, 1e2+3e4i
		complexRegex := regexp.MustCompile(`^[+-]?(?:(?:\d+\.?\d*(?:[eE][+-]?\d+)?(?:/\d+)?)?[+-])?\d*\.?\d*(?:[eE][+-]?\d+)?(?:/\d+)?i$`)
		if complexRegex.MatchString(s) {
			return COMPLEX_TYPE, nil
		}

		// If it contains 'i' but doesn't match complex patterns, it's invalid
		return 0, fmt.Errorf("unknown number format: %s", s)
	}

	// Check for real number (contains '.' or scientific notation 'e'/'E')
	if strings.Contains(s, ".") || strings.Contains(s, "e") || strings.Contains(s, "E") {
		// Validate with regex
		realRegex := regexp.MustCompile(`^[+-]?\d*\.?\d+(?:[eE][+-]?\d+)?$`)
		if realRegex.MatchString(s) {
			return REAL_TYPE, nil
		}
		return 0, fmt.Errorf("invalid real number format: %s", s)
	}

	// Check for rational number (contains '/')
	if strings.Contains(s, "/") {
		// Validate rational format with regex
		rationalRegex := regexp.MustCompile(`^[+-]?\d+/\d+$`)
		if rationalRegex.MatchString(s) {
			return RATIONAL_TYPE, nil
		}
		return 0, fmt.Errorf("invalid rational number format: %s", s)
	}

	// Check for integer or big integer
	// First validate integer format with regex
	integerRegex := regexp.MustCompile(`^[+-]?\d+$`)
	if integerRegex.MatchString(s) {
		_, err := strconv.ParseInt(s, 10, 64)
		if err != nil {
			// If it overflows int64, it's a big integer
			_, ok := new(big.Int).SetString(s, 10)
			if !ok {
				return 0, fmt.Errorf("invalid integer format: %s", s)
			}
			return BIGINTEGER_TYPE, nil
		}
		return INTEGER_TYPE, nil
	}

	// Invalid format
	return 0, fmt.Errorf("unknown number format: %s", s)
}

// getComplexParts extracts the real and imaginary parts of a complex number
func getComplexParts(s string) (realPart, imagePart string) {
	// Handle special cases
	if s == "i" || s == "+i" {
		return "", "1"
	}
	if s == "-i" {
		return "", "-1"
	}

	// Find the last '+' or '-' that is not part of scientific notation
	index := -1
	for i := len(s) - 1; i >= 0; i-- {
		if (s[i] == '+' || s[i] == '-') && i > 0 {
			// Make sure this +/- is not part of scientific notation
			isScientific := false

			// Look backward for 'e' or 'E'
			for j := i - 1; j >= 0; j-- {
				if s[j] == 'e' || s[j] == 'E' {
					isScientific = true
					break
				}
				// Stop if we encounter another +/- or non-numeric character
				if s[j] == '+' || s[j] == '-' || (!isdigit(s[j]) && s[j] != '.') {
					break
				}
			}

			if !isScientific {
				index = i
				break
			}
		}
	}

	// If no operator found or it's at the beginning, the whole string is the imaginary part
	if index <= 0 {
		// Remove the trailing 'i'
		imag := s[:len(s)-1]
		switch imag {
		case "", "+":
			imag = "1"
		case "-":
			imag = "-1"
		}
		// Remove leading '+' if it exists
		if len(imag) > 1 && imag[0] == '+' {
			imag = imag[1:]
		}
		return "", imag
	}

	// Split into real and imaginary parts
	realPart = s[:index]

	// Handle the imaginary part
	imagPartWithI := s[index : len(s)-1] // Exclude the trailing 'i'
	imagePart = imagPartWithI
	switch imagePart {
	case "", "+":
		imagePart = "1"
	case "-":
		imagePart = "-1"
	}

	// Remove leading '+' from imagePart if it exists (but not if it's a standalone "+")
	if len(imagePart) > 1 && imagePart[0] == '+' {
		imagePart = imagePart[1:]
	}

	return realPart, imagePart
}

// Helper function to check if a character is a digit
func isdigit(c byte) bool {
	return c >= '0' && c <= '9'
}
